#pragma once

#include <iostream>
#include <unistd.h>
#include "localprodconsmod.hpp"

namespace Task_Data2
{
    class Task_Add
    {
    public:
        Task_Add(int a, int b)
            : _a(a), _b(b)
        {
        }
        Task_Add()
        {
        }
        int Add()
        {
            return _a + _b;
        }

        std::string Description()
        {
            std::string buffer = std::to_string(_a) + " + " + std::to_string(_b) + " = ";
            return buffer;
        }

    private:
        int _a;
        int _b;
    };

    typedef Task_Add MyDataType;

    template <typename T>
    struct MyData
    {
        const char *_name;
        Local_Prod_Cons_Mod::LocalRingQueue<T> *_myqueue;
        MyData(const char *name, Local_Prod_Cons_Mod::LocalRingQueue<T> *myqueue)
            : _name(name), _myqueue(myqueue)
        {
        }
    };

    void *producer(void *argv)
    {
        MyData<MyDataType> *myproducer = static_cast<MyData<MyDataType> *>(argv);
        int a = 0;
        int b = 0;
        while (1)
        {
            Task_Add add(a, b);
            myproducer->_myqueue->Push(&add);
            std::cout << myproducer->_name << " send: " << add.Description() << std::endl;
            a++;
            b++;
            usleep(5000);
        }
    }

    void *consumer(void *argv)
    {
        MyData<MyDataType> *myconsumer = static_cast<MyData<MyDataType> *>(argv);
        while (1)
        {
            MyDataType data = myconsumer->_myqueue->Pop();
            int value = data.Add();
            std::cout << myconsumer->_name << " receive: " << data.Description() << value << std::endl;
            sleep(1);
        }
    }

    void linux_prodcons()
    {
        Local_Prod_Cons_Mod::LocalRingQueue<MyDataType> *myqueue = new Local_Prod_Cons_Mod::LocalRingQueue<MyDataType>(5);
        MyData<MyDataType> *myproducer = new MyData<MyDataType>("producer", myqueue);
        MyData<MyDataType> *myconsumer = new MyData<MyDataType>("consumer", myqueue);

        pthread_t pth1_id[2];
        pthread_create(&pth1_id[0], NULL, producer, (void *)(myproducer));
        pthread_create(&pth1_id[1], NULL, producer, (void *)(myproducer));
        pthread_t pth2_id[3];
        pthread_create(&pth2_id[0], NULL, consumer, (void *)(myconsumer));
        pthread_create(&pth2_id[1], NULL, consumer, (void *)(myconsumer));
        pthread_create(&pth2_id[2], NULL, consumer, (void *)(myconsumer));

        pthread_join(pth1_id[0], nullptr);
        pthread_join(pth1_id[1], nullptr);
        pthread_join(pth2_id[0], nullptr);
        pthread_join(pth2_id[1], nullptr);
        pthread_join(pth2_id[2], nullptr);

        delete myqueue;
        delete myproducer;
        delete myconsumer;
    }

}